package com.yupi.springbootinit.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.exception.ThrowUtils;
import com.yupi.springbootinit.mapper.ChartMapper;
import com.yupi.springbootinit.model.dto.chart.ChartEsDTO;
import com.yupi.springbootinit.model.dto.chart.ChartQueryRequest;
import com.yupi.springbootinit.model.dto.post.PostEsDTO;
import com.yupi.springbootinit.model.entity.Chart;
import com.yupi.springbootinit.model.entity.Post;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.vo.ChartVO;
import com.yupi.springbootinit.model.vo.UserVO;
import com.yupi.springbootinit.service.ChartService;
import com.yupi.springbootinit.service.UserService;
import com.yupi.springbootinit.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 李宏昊
 * @description 针对表【chart(图表信息表)】的数据库操作Service实现
 * @createDate 2024-08-21 12:53:22
 */
@Service
@Slf4j
public class ChartServiceImpl extends ServiceImpl<ChartMapper, Chart> implements ChartService {
    @Resource
    private UserService userService;
    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Override
    public void validChart(Chart chart, boolean add) {
        if (chart == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        final String goal = chart.getGoal();
        final String chartData = chart.getChartData();
        final String chartType = chart.getChartType();
        if (add) {
            ThrowUtils.throwIf(StringUtils.isAnyBlank(goal, chartData, chartType), ErrorCode.PARAMS_ERROR);
        }
        // 有参数校验
        if (StringUtils.isNotBlank(goal) && goal.length() > 50) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "目标过长");
        }
        if (StringUtils.isNotBlank(chartData) && chartData.length() > 50) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "数据过长");
        }
        if (StringUtils.isNotBlank(chartType) && chartType.length() > 50) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "类型过长");
        }
    }
    
    @Override
    public ChartVO getChartVO(Chart chart, HttpServletRequest request) {
        ChartVO chartVO = new ChartVO();
        Long chartId = chart.getId();
        //1.关联查询用户信息
        Long userId = chart.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        chartVO.setUser(userVO);
        // 2. 已登录，用户有关的图表的状态信息
        User loginUser = userService.getLoginUserPermitNull(request);
        return chartVO;
    }
    
    @Override
    public QueryWrapper<Chart> getQueryWrapper(ChartQueryRequest chartQueryRequest) {
        QueryWrapper<Chart> queryWrapper = new QueryWrapper<>();
        if (chartQueryRequest == null) {
            return queryWrapper;
        }
        Long id = chartQueryRequest.getId();
        Long notId = chartQueryRequest.getNotId();
        Long userId = chartQueryRequest.getUserId();
        String goal = chartQueryRequest.getGoal();
        Date createTimeBefore = chartQueryRequest.getCreateTimeBefore();
        Date createTimeAfter = chartQueryRequest.getCreateTimeAfter();
        Date updateTimeBefore = chartQueryRequest.getUpdateTimeBefore();
        Date updateTimeAfter = chartQueryRequest.getUpdateTimeAfter();
        List<String> chartDatas = chartQueryRequest.getChartDatas();
        String sortField = chartQueryRequest.getSortField();
        String sortOrder = chartQueryRequest.getSortOrder();
        if (CollUtil.isNotEmpty(chartDatas)) {
            for (String chartData : chartDatas) {
                queryWrapper.like("chart_data", "\"" + chartData + "\"");
            }
        }
        queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.like(StringUtils.isNotBlank(goal), "goal", goal);
        queryWrapper.le(ObjectUtils.isNotEmpty(createTimeBefore), "createTime", createTimeBefore);
        queryWrapper.ge(ObjectUtils.isNotEmpty(createTimeAfter), "createTime", createTimeAfter);
        queryWrapper.le(ObjectUtils.isNotEmpty(updateTimeBefore), "updateTime", updateTimeBefore);
        queryWrapper.ge(ObjectUtils.isNotEmpty(updateTimeAfter), "updateTime", updateTimeAfter);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField);
        return queryWrapper;
    }
    
    @Override
    public Page<ChartVO> getChartVOPage(Page<Chart> chartPage, HttpServletRequest request) {
        List<Chart> chartList = chartPage.getRecords();
        Page<ChartVO> chartVOPage = new Page<>(chartPage.getCurrent(), chartPage.getSize(), chartPage.getTotal());
        if (CollUtil.isEmpty(chartList)) {
         return chartVOPage;   
        }
        //1.关联查询用户信息
        Set<Long> userIdSet = chartList.stream().map(Chart::getUserId).collect(Collectors.toSet());
        Map<Long,List<User>> userIdUserMap = userService.listByIds(userIdSet).stream().collect(Collectors.groupingBy(User::getId));
        
        return null;
    }
    
    @Override
    public Page<Chart> searchFromEs(ChartQueryRequest chartQueryRequest) {
        Long id = chartQueryRequest.getId();
        Long notId = chartQueryRequest.getNotId();
        String searchText = chartQueryRequest.getChartType();
        String title = chartQueryRequest.getGoal();
        String content = chartQueryRequest.getGoal();
        List<String> tagList = chartQueryRequest.getChartDatas();
        List<String> orTagList = chartQueryRequest.getChartDatas();
        Long userId = chartQueryRequest.getUserId();
        // es 起始页为 0
        long current = chartQueryRequest.getCurrent() - 1;
        long pageSize = chartQueryRequest.getPageSize();
        String sortField = chartQueryRequest.getSortField();
        String sortOrder = chartQueryRequest.getSortOrder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 过滤
        boolQueryBuilder.filter(QueryBuilders.termQuery("isDelete", 0));
        if (id != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("id", id));
        }
        if (notId != null) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("id", notId));
        }
        if (userId != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("userId", userId));
        }
        // 必须包含所有标签
        if (CollUtil.isNotEmpty(tagList)) {
            for (String tag : tagList) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("tags", tag));
            }
        }
        // 包含任何一个标签即可
        if (CollUtil.isNotEmpty(orTagList)) {
            BoolQueryBuilder orTagBoolQueryBuilder = QueryBuilders.boolQuery();
            for (String tag : orTagList) {
                orTagBoolQueryBuilder.should(QueryBuilders.termQuery("tags", tag));
            }
            orTagBoolQueryBuilder.minimumShouldMatch(1);
            boolQueryBuilder.filter(orTagBoolQueryBuilder);
        }
        // 按关键词检索
        if (StringUtils.isNotBlank(searchText)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("title", searchText));
            boolQueryBuilder.should(QueryBuilders.matchQuery("description", searchText));
            boolQueryBuilder.should(QueryBuilders.matchQuery("content", searchText));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        // 按标题检索
        if (StringUtils.isNotBlank(title)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("title", title));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        // 按内容检索
        if (StringUtils.isNotBlank(content)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("content", content));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        // 排序
        SortBuilder<?> sortBuilder = SortBuilders.scoreSort();
        if (StringUtils.isNotBlank(sortField)) {
            sortBuilder = SortBuilders.fieldSort(sortField);
            sortBuilder.order(CommonConstant.SORT_ORDER_ASC.equals(sortOrder) ? SortOrder.ASC : SortOrder.DESC);
        }
        // 分页
        PageRequest pageRequest = PageRequest.of((int) current, (int) pageSize);
        // 构造查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder)
                .withPageable(pageRequest).withSorts(sortBuilder).build();
        SearchHits<ChartEsDTO> searchHits = elasticsearchRestTemplate.search(searchQuery, ChartEsDTO.class);
        Page<Chart> page = new Page<>();
        page.setTotal(searchHits.getTotalHits());
        List<Chart> resourceList = new ArrayList<>();
        // 查出结果后，从 db 获取最新动态数据（比如点赞数）
        if (searchHits.hasSearchHits()) {
            List<SearchHit<ChartEsDTO>> searchHitList = searchHits.getSearchHits();
            List<Long> chartIdList = searchHitList.stream().map(searchHit -> searchHit.getContent().getId())
                    .collect(Collectors.toList());
            List<Chart> chartList = baseMapper.selectBatchIds(chartIdList);
            if (chartList != null) {
                Map<Long, List<Chart>> idChartMap = chartList.stream().collect(Collectors.groupingBy(Chart::getId));
                chartIdList.forEach(chartId -> {
                    if (idChartMap.containsKey(chartId)) {
                        resourceList.add(idChartMap.get(chartId).get(0));
                    } else {
                        // 从 es 清空 db 已物理删除的数据
                        String delete = elasticsearchRestTemplate.delete(String.valueOf(chartId), ChartEsDTO.class);
                        log.info("delete chart {}", delete);
                    }
                });
            }
        }
        page.setRecords(resourceList);
        return page;
    }
}




